home *** CD-ROM | disk | FTP | other *** search
Text File | 1997-12-18 | 40.6 KB | 1,038 lines | [TEXT/ALFA] |
-
- Writing New Modes, Menus and Packages
-
-
- created: 3/3/97 {12:44:50 pm}
- last update: 18/12/97 {10:23:26 pm}
-
- Author: Vince Darley, some pieces by Tom Fetherston and Pete Keleher
- E-mail: <darley@fas.harvard.edu>
- mail: Division of Engineering and Applied Sciences, Harvard University
- Oxford Street, Cambridge MA 02138, USA
- www: <http://www.fas.harvard.edu/~darley/>
-
-
- Introduction
-
- If you're writing or modifying any mode, menu or extension (collectively
- known as packages) related Tcl code for use with Alpha, you should read this
- document. It also tells you how to make use of some of the features of
- Vince's Additions in your mode. These instructions pertain to all versions
- of Alpha greater than or equal to 7.0.
-
- There are three types of package which Alpha uses: modes, menus and
- extensions. A mode helps with editing a file for a particular purpose: web
- pages use 'HTML' mode, C++ code uses 'C++' mode, LaTeX documents use 'TeX'
- mode,… There are about 20 such modes currently available. Menus are of two
- types: mode-menus and global menus. Any global menu may also be a mode menu
- and any mode menu may also be a global menu, but there is a useful
- functional distinction: mode menus are only really useful when you are
- editing a document associated with that mode. Global menus are useful at
- any time, independent of the mode. Alpha designates such menus 'package
- menus', and has a separate menu for them allowing you to switch them on and
- off easily. Examples of package menus are the filesets menu, the eudora
- menu and the electric menu. When you create a new menu for Alpha, you have
- the option of making it a package menu or not. Finally, the third type of
- Alpha package is the extension. This is a piece of code which may be
- turned on or off by the user independently of the current mode. Examples of
- extensions are Vince's Additions, the printer choices sub-menu, or the
- bib-engine (used to interact with BibTeX).
-
- For the impatient reader: here's how to write a very simple extension
- which contains one new procedure and one new key-binding (to that
- procedure). Just create a file which looks like this:
-
- # (auto-install) --- this line will cause Alpha to try to install
- # this pkg when this file is opened outside of Alpha's folder hierarchy
-
- alpha::extension myPackage 0.1 {
- # bind the 'x' key to my procedure (not a good idea ;-)
- bind 'x' myProcedure
- } uninstall {this-file} maintainer {
- "My Name" my@email http://webpage..
- } help {
- Binds the blah-key to 'myProcedure' which carries out...
- }
-
- proc myProcedure {} {
- # do some cool stuff
- }
-
- Save this file on your desktop (say), and open it. You'll see Alpha
- automatically opens an installation dialog, puts this file in the
- right place if you agreed to the installation, and rebuilds its
- package and tcl indices so that this package can be used next time
- you restart Alpha (actually with a simple package like this, you can
- use it straight-away).
-
- Alpha provides lots of cool features to help you write useful
- packages, whether they are modes, menus or extensions. This
- document describes those features.
-
- Declaring your package to Alpha
-
- A package must contain, preferably as its first non-comment line (this
- is important), a statement like this:
-
- alpha::mode NAME VERSION ...
-
- alpha::menu NAME VERSION ...
-
- alpha::extension NAME VERSION ...
-
- (The other parameters to these commands are explained below). The name will
- identify your package, and for modes must be at most 4 characters long.
- It should not contain any spaces (this limitation may be lifted in a future
- version of Alpha). The version is a string of the form 1.0.1, or 2.3b1 or
- 1.4.530.1.3a5. Modes, menus and extensions take different arguments for the
- remainder of the 'alpha::' declaration line, but each ends in a script which
- Alpha scans and stores for you (Alpha scans all installed files for
- package declaration lines and caches this information so that at startup,
- no files need be read). For modes and menus, this script is executed
- automatically at startup. For extensions, the script is only executed if
- that extension has been activated by the user. Package initialisation
- occurs in the order: modes, menus and finally extensions.
-
- IMPORTANT: The declaration command must be at the beginning of the line, and
- it must not be wrapped in any 'catch' statements. This is necessary to
- allow Alpha to rebuild package indices rapidly. If you wish to write
- backwards compatible code, try something like:
-
- if {[info commands alpha::extension] != ""} {
- alpha::extension ...
- } else {
- # initialise in some old way
- }
-
- Your package will not function properly if you don't obey the above
- guidelines. Alpha itself is considered a package, with a version
- number, so that your code can request a particular version of Alpha.
- Alpha's version number also has a patchlevel which will be updated
- with each Tcl-only patch release. Hence you can write:
-
- alpha::package require Alpha 7.0b1
-
- For the first Alpha 7.0 release, and
-
- alpha::package require Alpha 7.0b1p1
-
- If your package actually requires some fixes from the first patch release.
- You can similarly require particular versions of other packages. You
- should 'require' as old a version as possible, so that you don't force
- users to upgrade unnecessarily.
-
- Note that with the advent of the new alpha:: commands, it is no longer
- necessary to place modes, menus and packages in their separate directories:
- they can go anywhere on the auto-path. However it is more convenient to
- store them separately most of the time.
-
- The developer utilities package
-
- You will want to download this package, since it helps with a number
- of developer-related tasks:
-
- • distribution archival, compression, and uploading
- • colouring and hyperlinking Help files (like this one)
-
- For anyone helping with Alpha's core distribution it also allows:
-
- • colouring Alpha's manual, commands, readme and changes files
-
- ===============================================================================
-
- Writing New Modes
-
- To add a mode to Alpha, a file (usually ending with 'Mode.tcl') must be
- created and placed in the ":Tcl:Modes" directory.
-
- The file should begin with a construct of the following form:
-
- alpha::mode Perl 1.3 dummyPerl {*.pl *.ph *.pm} perlMenu {
- addMenu perlMenu •132
- }
-
- This command is very, very important.
-
- alpha::mode <mode> <version> <dummyProc> <suffixes> <mode-menus> <script>
-
- defines a new mode. When trying to switch to the Perl mode, Alpha will
- attempt to execute the function 'dummyPerl'. The suffixes allow Alpha to
- automatically determine the correct mode of a newly opened file. In this
- case the script contains the single command:
-
- addMenu <mname> <name>
-
- which defines a new menu, with 'name' the visible name of the menu (names
- which start with '•' indicate Alpha should use an icon resource with the
- given number. Icon number 132 is the Perl camel icon). This menu can be
- used in any mode, although by default, it is only attached to Perl mode.
- 'mname' is actually a variable which contains (will contain) the real menu
- name (in this case '•132').
-
- Perhaps the MOST important part of the above code is the existence of the
- 'dummyProc'. When this proc is called, the result must be that all of
- the mode's preferences are declared. In other words, the dummyProc
- should normally be in the same file as the mode's 'newPref' declarations.
- This is important because almost directly after that call, Alpha expects
- all of the mode's preferences to be stored in the ${mode}modeVars array,
- which will only be true if all of the newPref commands have been evaluated.
-
- Here is an example from Diff mode:
-
- alpha::mode Diff 1.0 diffMenu {*.diff} {diffMenu} {
- addMenu diffMenu •288
- menu::insert Utils submenu 0 compare
- menu::insert compare items end "windows" "files…" "directories…"
- } uninstall {
- removeFile "$pkg_file"
- removeFile "${HOME}:Tools:GNU Diff"
- } maintainer { "Vince Darley" darley@fas.harvard.edu http://... }
-
- The 'uninstall' and 'maintainer' sections are optional, and explained later.
- Here is a more complex example for Python mode:
-
- # ◊◊◊◊ minalmalist mode set-up ◊◊◊◊ #
- alpha::mode Pyth 0.2 dummyPython {*.py *.pyc *.pyi} PythonMenu {
- addMenu PythonMenu
- #To set the mode from a unix-like "#!python" first line
- set unixMode(python) {Pyth}
- }
- # dummy proc to load this mode.
- proc dummyPython {} {}
- # dummy proc to load the code to make the PythonMenu
- proc PythonMenu {} {}
- # rest of mode's code follows...
-
- #Let the automatic comment insertion/continuation
- # routines function with this mode.
- set Pyth::commentCharacters(General) "\#"
- set Pyth::commentCharacters(Paragraph) [list "## " " ##" " # "]
- set Pyth::commentCharacters(Box) [list "#" 1 "#" 1 "#" 3]
-
- The package declaration should contain all code which is necessary to
- recognise a given file as belonging to that mode (hence the use
- of 'unixMode' for python), which will then make Alpha call the
- dummyProc which will auto-load the entire file. Other information,
- such as the 'commentCharacters' entries above should not go in the
- package declaration.
-
- Notice that there are two types of 'dummy' proc: each menu Alpha uses
- should have a proc of the same name associated with it. This proc is
- called by Alpha _each_ time Alpha tries to insert the menu into the
- menubar. The proc can be empty (as above), or could actually do
- something if desired. The second kind of dummy proc is the 'mode'
- dummy proc, given in the 'alpha::mode' command. Here it is called
- 'dummyPython'. Alpha calls this proc each time it switches to Pyth
- mode. Again the proc can do something if desired, but will usually
- be empty. If both procs are empty, as above, one can of course
- just use one proc (called PythonMenu in this case), and replace the
- alpha::mode line by:
-
- alpha::mode Pyth 0.2 PythonMenu {*.py *.pyc *.pyi} PythonMenu
-
- The only advantage of this approach is that it saves a small amount
- of memory (you can delete the 'dummyPython' proc from the file).
-
- Multi-file modes
-
- Modes that consist of more than a single file should no longer use a source
- statement that assumes that the other files for the mode will be in
- $HOME:Tcl:Modes. The best solution is to use Alpha's standard auto-loading
- capability which will source a file when it needs a procedure which is
- contained in that file. If you must use 'source' manually, you can use
- 'file dirname [procs::find someProcInThisFile]' to get the current
- directory. Your other files should also be there.
-
- A convenient way of implementing your multi-file loading is to create
- procs with the same name as the file at the beginning of each file.
- Then to load the file you just do 'catch "filename"'. For example
- if there is a proc defined 'proc perl4.tcl {} {}' at the start of
- the file "perl4.tcl", then I can auto-load that file with:
-
- if [catch perl4.tcl] {
- alertnote "Problem loading 'perl4.tcl'"
- }
-
- Remember, you don't necessarily need to source all your mode/pkg's files in
- one go. Tcl is designed to source files for you when they are needed (when
- a procedure contained in one of them is called). Hence you only need to
- source files which are required immediately (to set up some data, variables,
- menus etc.) and not everything else. It is usually best to have a single
- file which contains all the initialisation code, and let any other files be
- auto-loaded as necessary.
-
- Mode procs
-
- The following procs are either required or desired for a mode to be
- fully functional within Alpha.
-
- <mode>::MarkFile
- <mode>::parseFuncs
-
- <mode>::DblClick
-
- <mode>::optionTitlebar
- <mode>::optionTitlebarSelect
-
- <mode>::indentLine
- <mode>::electricLeft
- <mode>::electricRight
- <mode>::correctIndentation
- <mode>::carriageReturn
-
- Have a look at a standard mode like Tcl or C++ to see what these
- should do. <mode>::correctIndentation must not fail.
-
- Hooks
-
- Do not do all that 'rename saveHook mySaveHook'... stuff. Use
- 'hook::register' instead. See the file "hook.tcl" for details, but all
- you need to do is add lines like these:
-
- hook::register saveHook modified "C" "C++"
- hook::register saveHook modified "Pasc"
- hook::register saveHook htmlLastModified HTML
- hook::register savePostHook codeWarrior_modified "C++" "C"
- hook::register savePostHook ftpPostHook
- hook::register saveasHook htmlLastModified HTML
-
- Here's the general form
-
- hook::register 'hook-name' 'your proc' 'mode' ?... 'mode'?
-
- If you don't include a 'mode', then your proc will be called no
- matter what the current mode is. Avoid this unless absolutely
- necessary. Here are the current hooks:
-
- activateHook changeMode closeHook deactivateHook modifyModeFlags
- quitHook resumeHook saveasHook saveHook savePostHook suspendHook
- openHook
-
- There's also a 'mode::init' hook which will be called the first
- time a mode is started up. Note that the mode exists, but its
- variables have not yet been made global, and its menus have not
- yet been inserted into the menu bar.
-
- There's also a 'startupHook' which is called when Alpha starts
- up, but after all other initialisation has taken place, a 'launch'
- hook which is called when Alpha launches another application
- (register with hook::register launch yourproc $sig).
-
- Smart mode lines
-
- If your mode will want to be able to use the first line of a file to
- determine what mode a file should be opened up in, you need to tell
- alpha what word in the first line should trigger that mode:
-
- set unixMode(python) {Pyth}
-
- A good place to do this is in the body of the your mode's package
- declaration "alpha::mode … {…" statement (see example for Python above).
- Note that the presence of the word itself is not sufficient; it must be of
- the form '#!\usr\..\python' as is common on Unix (where it tells the shell
- with what application to run the script)
-
- Mode creator types
-
- If your mode wants to declare itself as a default for files with a
- particular creator, you need to tell Alpha with an entry like this:
-
- set modeCreators(GPLT) GPLT
-
- A good place to do this is in the body of the your mode's package
- declaration "alpha::mode … {…" statement.
-
- Comment characters
-
- If your mode will want to use the standard Alpha comment/uncomment
- block procedures, file headers, ... you need to tell Alpha what
- characters are used for comments. Rather than redefining
- the procedure 'commentCharacters', you should just define the following
- variables:
-
- set commentCharacters(${mode}:General) [list "*" "//"]
- set commentCharacters(${mode}:Paragraph) [list "/* " " */" " * "]
- set commentCharacters(${mode}:Box) [list "/*" 2 "*/" 2 "*" 3]
-
- or
-
- set ${mode}::commentCharacters(General) [list "*" "//"]
- set ${mode}::commentCharacters(Paragraph) [list "/* " " */" " * "]
- set ${mode}::commentCharacters(Box) [list "/*" 2 "*/" 2 "*" 3]
-
- where the values shown are for C++ mode. If you do this then there is
- no need to mess with the commentCharacters procedure. (In general it
- is best if your mode does not need to redefine procedures in Alpha's core).
-
- Paragraph definitions
-
- Paragraph filling. You can set the variables:
-
- set ${mode}::startPara {^(.*\{)?[ \t]*$}
- set ${mode}::endPara {^(.*\})?[ \t]*$}
-
- to customize your mode's paragraph definition. This example is for Tcl
- mode.
-
- Electric braces and semicolon
-
- If your mode uses electric '{', '}', ';' (i.e. characters that end the
- current line and indent the next one automatically) you need to define
- a few mode variables, for instance like this:
-
- newPref flag elecRBrace 1 C++
- newPref flag electricSemi 1 C++
- newPref flag elecLBrace 1 C++
-
- Then you just need to define any of the procedures '${mode}::electricLeft',
- '${mode}::electricRight' and '${mode}::electricSemi' which will be called
- automatically (you do NOT need to bind anything to the keys). If you do
- not define these procedures, Alpha will use a default electric procedure
- which works pretty well for C, Perl and Java code.
-
- Do not bind to '{', '}' or ';' if you want these to be electric. Alpha
- will automatically call your mode's procedures if they are named
- correctly.
-
- Option-click-titlebar menu
-
- If your mode has a specific 'opt-titlebar-click' menu, you need to
- define the procedures:
-
- proc C++::OptionTitlebar {} {
- # returns list of items for the menu
- }
- proc C++::OptionTitlebarSelect {item} {
- # carries out the mode-specific action when 'item' is selected.
- }
-
- Electric code templates
-
- If you mode wants to insert text into the window which contains template
- stops (usually bullets '•' in Alpha), so that the user can move from
- one to the next using the standard Alpha template packages (Alpha comes with
- a basic one, and more sophisticated ones build upon the same
- infrastructure), the you should insert template text with:
-
- elec::Insertion "blah blah •• blah blah"
-
- This is a simple example with a single template stop. Template stops are
- noted with a pair of bullets (even though only one appears in the text).
- You can place between the pair of bullets some more information about the
- template stop, for instance:
-
- elec::Insertion "while \{•condition•\} \{\r\t•while body•\r\}\r••"
-
- would be useful to insert a typical Tcl 'while' loop. The template
- packages can prompt the user with the explanatory text making code
- entry a little bit easier.
-
- The 'elec::Insertion' routine works just like 'insertText' except it treats
- any item •PROMPT• as a template stop called 'PROMPT'. This procedure takes
- a variable number of arguments, just like 'insertText'. It has one further
- side-effect. If there are any stops in the block, then the cursor is
- positioned at the first such stop. Hence you don't need to do this: set p
- [getPos] ; insertText "blah..." ; goto $p ; nextTabStop Instead you just do
- 'elec::Insert "blah..."'. Note that the procedure 'nextTabStop' no longer
- exists. Use "ring::+", "ring::-" etc. to move amongst tab stops. The
- basic Alpha distribution contains only basic template support. Install
- Vince's Additions to extend this support to persistent stops, with
- user-prompting in the text or status bar,... You don't have to change your
- code to take advantage of the features of V'sA. It comes for free if you use
- 'elec::Insertion' etc.
-
- If you wish to use electric templates, avoid binding anything to the 'tab'
- key or the 'j' key (also opt-tab, cmd-tab,…). Such bindings may conflict
- with the electric bindings.
-
- To use electric templates, you'll want to add a mode preference for
- electricTab with 'newPref flag electricTab 1 <mode>'. If your mode
- never wants the Tab key to indent, then define a dummy '<mode>::indentLine'
- proc which is empty: 'proc <mode>::indentLine {} {}'.
-
- Electric return
-
- This allows pressing return to indent correctly for the following line so
- you may begin typing immediately. To use this add a pref:
-
- newPref flag elecReturn 1 C++
-
- and do not bind to the return key.
-
- Automatic indentation
-
- Two variables are associated with a window's indentation scheme:
- 'indentationAmount' and the window's tab-size (which can be read
- with 'getWinInfo' or 'text::getTabSize'). The flag 'indentFollowsTabs'
- declares whether the standard indentation shift is the tab-size (in
- which case 'indentationAmount' is ignored) or whether to follow that
- variable. If you are writing a custom indentation routine, the
- procedure 'indent::setup' will be useful to handle all these choices
- for you.
-
- The marks menu
-
- Each mode has a procedure <mode>::MarkFile which is called to create the
- popup 'M' menu of marks. There is a global flag 'quietlyClearMarks'
- which is set to 1 which dictates that the marks should be rebuilt
- without prompting the user as necessary. You can add a mode-pref
- to over-ride this if you want.
-
- <mode>Completions.tcl
-
- Each mode can have a completions file for use by the 'elecCompletions'
- package (part of Vince's Additions or available separately). To use
- this, place the appropriate definitions in a file called
- '<mode>Completions.tcl'. The installer will place such files in the
- 'Completions' directory automatically (provided you don't put them in
- a sub-folder of your distribution), and they will also be sourced
- automatically the first time a file opens in your mode. There is
- therefore no need for you to source the file yourself.
-
- ===============================================================================
-
- Writing new menus
-
- New menus are placed in ":Tcl:Menus", and contain a start-up
- section of much the same form as a mode:
-
- alpha::menu ftpMenu 0.3 "•141" in_menu {
- }
- # proc ftpMenu to auto-load
- proc ftpMenu {} {}
-
- The 'in_menu' paramter tells Alpha that this menu should go in the
- 'package menus' menu for quick selection. If the parameter is set to
- any other value, the menu is not registered there (but can still be
- turned on or off using the Global->Menus dialog).
-
- NOTE: If all you want to do is add a submenu to an already existing
- menu, go to the section 'Adding Items to Global Menus'.
-
- A menu-package is a set of code which builds and handles a standalone menu
- which the user may choose as a global menu. Examples are the ftpMenu,
- filesetMenu, voodooMenu, internetConfigMenu, colorMenu and eudoraMenu (in
- fact this last item, since it has a mode associated with it, could in fact
- be rewritten as a mode with attached menu). Menus use a different startup
- scheme to extensions in that it is assumed a proc with the same name as the
- menu has been defined, and that calling that proc will build the menu.
- So the full setup will look like this
-
- alpha::menu ftpMenu 0.35 "•141" in_menu {} uninstall this-file
- # proc ftpMenu to auto-load
- proc ftpMenu {} {}
- # ...
-
- ===============================================================================
-
- Writing new extensions
-
- A new extension must provide at the very least the following line, preferably
- as the first non-comment line of one of its files:
-
- alpha::extension 'NAME' 'VERSION'
-
- It is better, if possible, if the extension can provide a small script to
- carry out initialisation (which occurs when Alpha starts up, if the user
- has turned the package on). If provided Alpha will use that script
- rather than sourcing the entire extension file. This means Alpha will
- start up more quickly. Such a script is given by the following line:
-
- alpha::extension 'NAME' 'VERSION' 'SCRIPT'
-
- Here is an example from the 'bibtexEngine' package:
-
- alpha::extension bibtexEngine 1.6 {
- eventHandler GURL GURL GURLHandler
- }
-
- The simple 'extension' command makes it very, very easy to extend Alpha's
- functionality without messing with the user's preferences file, without
- creating any '...+.tcl' extension files and without a complex installation
- process. Alpha simply maintains a database of all 'extension' scripts, and
- evaluates at startup all scripts for extensions which the user has activated.
-
- Writing new extensions
-
- Writers of any package for Alpha should pay some attention to the
- problems which can arise with international keyboards. Some bindings
- are simply not available on some keyboards. For instance, on some
- keyboards, you need to use 'shift' to get the key '\' (unlike
- american keyboards where it is a single keypress). On such a keyboard
- there is no distinction between 'cmd-\' and 'shift-cmd-\'. There is
- no simple workaround for this problem.
-
- Possibilities are: (i) check the current keyboard definition and adjust
- bindings appropriately (based upon user feedback, presumably). (ii) let
- the bindings be user-definable either by using 'newPref binding' to
- define things, or by using a menu-scheme such as is used by HTML mode.
-
- ===============================================================================
-
- Package preferences
-
- Alpha stores preferences in three different places:
-
- 1) Global preferences are stored in the global->preferences menu, and
- are for variables/flags which maintain a value at the global scope.
-
- 2) Mode preferences are stored in the mode->preferences… item, and
- are for variables/flags which are stored in a mode array, but are transfered
- into global scope when that mode is active (and hence temporarily override
- any global preferences with the same names)
-
- 3) Packages may add to the global/mode preferences as they desire. They
- may also store preferences in their package array '${pkg}modeVars(…)'. Such
- variables/flags are never transfered into the global scope. Menu items to
- edit a package's preferences should be placed in the 'global' menu, unless
- they are global/mode prefs which should be added to Alpha's default
- routines for use by the standard Alpha dialogs. There is a standard proc
-
- package::addPrefsDialog Mypkg
-
- which you can use to add an item to the global menu which will bring
- up the standard dialog to edit the contents of your '${pkg}modeVars(…)'
- array.
-
- Adding to the core prefs dialogs
-
- If you wish to add items to any of the core preferences pages (Backups,
- Electric, Miscellaneous,...), you can do that like this:
-
- lunion varPrefs(Electric) var1 var2
- lunion flagPrefs(Electric) flag1 flag2
-
- All non-registered global preferences are added to the Miscellaneous page,
- so there is no need to do that automatically. Make sure you don't add
- too much to any of these pages, because they will become too large to
- display correctly!
-
- You can also add new core preferences pages. All you have to do is create
- a new 'flagPrefs' entry (Alpha uses the command 'array names flagPrefs' to
- list the different pages):
-
- lunion flagPrefs(NewPage) flag1
-
- Only add such pages if your package really does merit it; otherwise you're
- better off just add a new global preferences dialog in the global menu.
-
- Defining a package's flags and variables
-
- Preferences for a mode or package are defined as follows:
-
- newPref type name {val 0} {pkg "global"} {pname ""} \
- {options ""} {subopt ""}
-
- Define a new preference variable/flag.
-
- 'type' is one of:
- 'flag' (on/off only), 'variable' (anything), 'binding' (key-combo)
- 'menubinding' (key-combo which works in a menu), 'file' (input only),
- 'io-file' (either input or output)
-
- 'name' is the var name,
-
- 'val' is its default value (which will be ignored if the variable
- already has a value)
-
- 'pkg' is either 'global' to mean a global preference, or the name
- of the mode or package (no spaces) for which this is a preference.
-
- 'pname' is a procedure to call if this preference is changed by
- the user (no need to setup a trace). This proc is only called
- for changes made through prefs dialogs or prefs menus created by
- Alpha's core procs. Other changes are not traced.
-
- Depending on the previous values, there are two optional arguments
- with the following uses:
-
- TYPE:
-
- variable:
-
- 'options' is a list of items from which this preference takes a single
- item.
- 'subopt' is any of 'item', 'index', 'varitem' or 'varindex' or 'array', where
- 'item' indicates the pref is simply an item from the given list
- of items, 'index' indicates it is an index into that list, and
- 'var*' indicates 'items' is in fact the name of a global variable
- which contains the list. 'array' means take one of the values from an array.
- If no value is given, 'item' is the default
-
- binding:
-
- 'options' is the name of a proc to which this item should be bound.
- If options = '1', then we bind to the proc with the same name as
- this variable. Otherwise we do not perform automatic bindings.
-
- 'subopt' indicates whether the binding is mode-specific or global.
- It should either be 'global' or the name of a mode. If not given,
- it defaults to 'global' for all non-modes, and to mode-specific for
- all packages. (Alpha tests if something is a mode by the existence
- of modeMenus($mode))
-
- menubinding:
-
- menubindings are like bindings, but they don't have any automatic
- binding capabilities, and are restricted to key-sequences which the
- MacOS allows in menus. Here is an example of how one might declare
- the 'QuickFind(Regexp)' dynamic pair using a menubinding pref:
-
- declare the binding:
-
- «Alpha ƒ» newPref menubinding quickFind/quickFindRegexp <B/S
-
- edit it if we like with:
-
- «Alpha ƒ» dialog::getAKey quickFind/quickFindRegexp <B/S
-
- show the menu sequence if we like:
-
- «Alpha ƒ» menu::bind quickFind/quickFindRegexp -
- <S<E<B/SquickFind <S<I<B/SquickFindRegexp
- «Alpha ƒ»
-
- add it to a menu:
-
- «Alpha ƒ» eval menu::insert Search items end \
- [menu::bind quickFind/quickFindRegexp -]
-
- Have a look at the search menu. It has a new dynamic item at the bottom!
-
- Adding items to global menus
-
- Using 'addMenuItem' is a bad idea, since many menus are dynamically
- rebuilt and such items will be lost. Furthermore, addMenuItem does
- not work if you want to add dynamic items or sub-menus. Also
- creating a menu directly using 'menu -n Name {list of items}' is
- generally a bad thing to do when using Alpha version 7.0 or newer.
-
- The solution to these problems is to use the following calls:
-
- menu::buildProc 'menu' 'procname'
- menu::insert 'menu' 'type' 'where' 'item' ?item...?
-
- For technical reasons, if you use both types of call, always add the procs
- first. You can add any list of items using the latter of these two calls.
- The first registers a procedure which will be called to build a given menu.
-
- Menus must be rewritten to support this new feature. Currently all
- global menus File...Config support it, and several modes: Tcl, Perl,
- TeX.
-
- menu::buildProc
-
- Register a procedure to be the 'build proc' for a given menu. This
- procedure can do one of two things:
-
- i) build the entire menu, including evaluating the 'menu ...' command.
- In this case the build proc should return anything which doesn't
- begin 'build ...' If the proc returns anything beginning with
- 'menu ..' that returned string is evaluated, but no insertions can
- take place.
-
- ii) build up part of the menu, and then allow pre-registered menu
- insertions/replacements to take-effect. In this case the procedure
- should return a list of the following (listed by index in the list):
-
- 0: "build"
- 1: list-of-items-in-the-menu
- 2: menu procedure to call when an item is selected. If nothing is given,
- or if '-1' is given, then we don't have a procedure. If "" is given,
- we use the standard 'menu::generalProc' procedure. Else we use the
- given procedure.
- 3: list of submenus which need building.
- 4: over-ride for the name of the menu.
-
- You must register the build-proc before attempting to build the menu.
- Once registered, any call of 'menu::buildSome name' will build your
- menu.
-
- menu::insert
-
- name, type, where, then list of items. type = 'items' 'submenu'
-
- Add given items to a given menu, provided they are not already there.
- Rebuild that menu if necessary.
-
- There are also procs 'menu::removeFrom' which does the opposite of
- this one, and 'menu::replaceWith' which replaces a given menu item
- with others.
-
- There is a difference between 'menu::insert Utils submenu 2 compare' and
- 'menu::insert Utils items 2 [list menu -n compare {}]'. The former
- registers the submenu as a submenu which will be built automatically by a
- call to 'menu::buildSome' each time the parent menu is rebuilt, the latter
- does no such thing. You will, therefore normally wish to use the first
- form, but occasionally there will be situations when the latter would be
- better.
-
- Here is a simple example:
-
- alpha::extension compareWindows 0.1 {
- bind 0x32 <X> compare::windowsInPlace
- bind '1' <X> compareOpt
- bind 0x32 <sX> compareNext
- bind 0x12 <sX> compareOptNext
- menu::insert Utils submenu 2 compare
- menu::insert "compare" items end windowsInPlace
- }
-
- We first add a submenu after the second item in the Utils menu, called
- 'compare', and then add to the end of that compare menu. This code works
- whether the package is active at startup or not. Here is a more
- complex example:
-
- alpha::extension documentProjects 1.2 {
- alpha::package require elecCompletions
- alpha::package require newDocument
- menu::buildProc "Current Project" Docproj::currentMenu
- menu::insert global items end \
- "documentProjectPrefs…" "userDetails…" \
- "<E<SremoveDocumentTemplate…" "<S<BeditDocumentTemplate…" \
- "<SnewDocumentTemplate…" \
- "<E<SremoveProject…" "<S<BeditProject…" "<SnewProject…"
- menu::insert global submenu end {Current Project}
- newPref binding updateFileVersion "/f<U" Docproj
- menu::insert fileUtils items end \
- "showInFinder" \
- "(-" \
- "updateDate" \
- "[menu::bind DocprojmodeVars(updateFileVersion) -]"
- lunion elec::MenuTemplates "createHeader" "newDocument"
- menu::insert elec items end \
- {menu -n functionComments -p menu::fileUtils {
- "/efunctionComment"
- "/e<IfunctionCommentSimple"
- "/e<OfunctionCommentWithAuthor"
- "/e<UfunctionCommentUpdate"
- }}
- set newDocument::handlers(documentProjects) Docproj::newHandler
- }
-
- The 'documentProjects' package adds items to many different menus,
- including the 'elec' menu (from the elecCompletions package).
-
- ===============================================================================
-
- Package testing
-
- The 'alpha::package' command is very similar to Tcl 8.0's standard
- 'package' command, but differs in a few respects. When Alpha upgrades
- to Tcl 8, this will allow both features to coexist happily. You can use
- 'alpha::package' to check/request the presence of other packages.
-
- alpha::package require NAME ?VERSION?
-
- Other sub-commands are 'exists' 'names' 'versions' 'vcompare' 'vsatisfies'
- 'forget' 'uninstall' and 'mode', 'menu' and 'package'. These last three
- mimic the usual alpha::mode alpha::menu and alpha::package commands.
-
- alpha::package require ?-extension -mode -menu? name version
- alpha::package exists ?-extension -mode -menu? name version
- alpha::package names
- alpha::package uninstall name version [this-file|this-directory|script]
- alpha::package vcompare v1 v2
- alpha::package vsatisfies v1 v2
- alpha::package versions ?-extension -mode -menu? name
- alpha::package type name
- alpha::package info name
- alpha::package maintainer name version {name email web-page}
- alpha::package help name version [file 'name'|text]
-
- Equivalent to alpha::mode, alpha::menu and alpha::extension
-
- alpha::package mode ...
- alpha::package menu ...
- alpha::package extension ...
-
- For extensions only:
-
- alpha::package forget name version
-
- ..
-
- ===============================================================================
-
- Installation
-
- There is a new install mode 'Inst' which adds the Install menu.
- Install mode is trigerred when a file's name ends in 'Install'
- or 'INSTALL', or when the first line of the file contains the
- letters 'install', provided in this last case, that the file
- is not in Alpha's Tcl hierarchy. This last case is useful so
- that a single .tcl file can be a package and be installed by
- Alpha using these nice scripts, without the need for a separate
- install-script-file. However once that .tcl file is installed,
- if you open it you certainly wouldn't want it opened in Install mode!
-
- So, single file packages should just include 'install' somewhere in
- their first line. Multi-file packages should include an install
- file. Call this file 'OPEN TO INSTALL' or something like that.
- When the user opens it, Inst mode is activated, and the user can
- use the install menu to install your package. If you wish the
- installation dialog to be activated automatically, include the
- text (auto-install) in the first line of the file.
-
- Most packages will _not_ need anything other than the existence of
- such a file. In fact a file called 'OPEN TO INSTALL' containing the
- single line '(auto-install)' will do the trick nicely.
-
- Alpha will scan the installation file directory and make a nice
- dialog with 'Easy install' and 'Custom install' options. Alpha
- knows where Modes, Menus, Completions, Bug fixes, Tools, Packages,
- Extensions, ... all go in the Alpha hierarchy.
-
- Package-specific installation over-rides
-
- You can over-ride the default behaviour by providing a 'xxx_install.tcl'
- file in the file directory. In such a case that file will be sourced.
- See "install.tcl" for some more information on how to over-ride the
- default behaviour. You will usually use the following procedure:
-
- install::packageInstallationDialog 'NAME' 'DESCRIPTION' ...
-
- Optional arguments are as follows:
-
- -ignore {list of files to ignore}
- -remove {list of files to remove from Alpha hierarchy}
- -forcequit '0 or 1'
- (forces the user to quit; default 0)
- -require {Pkg version Pkg version …}
- e.g. -require {Alpha 7.0b1p2 elecCompletions 7.99}
- -provide {Pkg version Pkg version …}
-
- and
-
- -SystemCode -Modes -Menus -BugFixes -Completions -Packages
- -ExtensionsCode -UserModifications -Tools -Home
-
- which force the placement/use of the following lists of files. To
- require an exact package version use:
-
- -require {Alpha {-exact 7.0b2} elecCompletions {-exact 8.1.2} ...}
-
- Also, rather than having separate 'OPEN-TO-INSTALL' and '*install.tcl'
- files, if the former file contains the text 'auto-install-script' in
- its first line, it will be used as a Tcl script, and sourced rather than
- opened. Ensure that first line begins with a '#' or an error will
- result. (You can open that file for editing, without triggering the
- install script if you hold down a modifier key).
-
- If you gave the -provide option, Alpha checks those items with what
- the user has already installed and warns if an item has already been
- installed and is not older than the one about to be installed.
-
- Uninstalling packages
-
- Each package should provide a 'alpha::package uninstall name version script'
- statement. When your script is evaluated, the global variable 'pkg_file'
- will be initialised to the full name of the file which contains the
- uninstall command. Therefore for a single file package, the following is
- normal:
-
- alpha::package uninstall developerUtilities 1.1 {removeFile $pkg_file}
-
- However, a much more convenient form of the above command is also possible,
- and most packages use it --- you may combine declaration and uninstall lines
- like this:
-
- alpha::extension developerUtilities 1.1 {
- # declaration script
- } uninstall {
- # uninstall script
- }
-
- i.e. there are two extra optional arguments to the 'package' command.
- Finally to be even simpler, if the command is 'uninstall this-file',
- then that is equivalent to {removeFile $pkg_file}, and if the command
- is 'uninstall this-directory', then that entire file's directory is
- removed. Make sure you don't use 'uninstall this-directory' for a
- single-file package, or you'll wipe out the entire package hierarchy.
- Similarly alpha::mode and alpha::menu commands may contain an optional
- uninstall script like the above.
-
- Disabling packages
-
- A package can add a script to be evaluated when the user disables the
- package. You do that with the additional command 'disable':
-
- alpha::extension developerUtilities 1.1 {
- # declaration script
- } disable {
- # disable script
- }
-
- Complex packages will probably not provide such a script. In such a
- case the user would have to restart Alpha to disable the package
- correctly.
-
- Tcl index files
-
- You probably know that Tcl uses 'index' files to find procedures which
- are called but not yet defined. Your installation directories may
- contain index files if you desire, but they are only installed if no
- current index file exists in the installation location. You cannot
- override this behaviour.
-
- ===============================================================================
-
- Vince's Additions Support:
-
- This is primarily for new modes.
-
- Source-Header files
-
- If your mode makes distinctions between 'Source' and 'Header'
- files, you should define these two variables
-
- newPref var sourceSuffices { .cc .cp .cpp .c .icc } C++
- newPref var headerSuffices { .h .hh } C++
-
- Completions
-
- If you mode is to use a variety of completion routines, define
- an array entry like this:
-
- set completions(${mode}) \
- {completion::cmd completion::electric completion::word}
-
- For the meaning of the list items, look at "elecCompletion.tcl". If
- all you need is the basic 'Command', 'Electric' and 'Word' completion
- routines, the above list will do the trick. You will then need to
- define a variable ${mode}cmds like this:
-
- set Ccmds { #elseif #endif #include class default enum for register return
- struct switch typedef volatile while }
-
- It MUST be in alphabetical order, and both start and end with a
- whitespace character. For electric template insertions, you need
- to create an array with entries like these:
-
- set Celectrics(for) " (•init•;•test•;•increment•)\{\n\t•loop body•\n\}\n••"
- set Celectrics(while) " (•test•)\{\n\t•loop body•\n\}\n••"
- set Celectrics(switch) " (•value•)\{\n…case •item•:\n\t•case body•\n…default:\n\t•default body•\n\}\n••"
- set Celectrics(case) " •item•:\n…•case body•\ncase"
-
- Mode-specific completions
-
- If your mode has its own completion routines, they must be named
- ${mode}::Completion::Type, where 'Type' is an entry in the above
- list. You'll have to know a reasonable bit of Tcl to write your
- own routines like that. Look at C::Completion::Class for a relatively
- simple example.
-
- Electric menu templates
-
- ${mode}Templates is a list of names which are added to the electric
- menu's 'Templates' sub-menu. The real procs should be called
- 'file::${name}'.
-
- Vince's Additions summary
-
- That's it! Take a look at "scilabMode.tcl" as a simple example of a new mode
- which makes use of Vince's Additions.
-
- ===============================================================================
-
-